package assignment04;

import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.set.TIntSet;

public class IntCounterMap {

	TIntObjectMap<IntCounter> counterMap;

	int currentModCount = 0;
	int cacheModCount = -1;
	double cacheTotalCount = 0.0;
	
	protected IntCounter ensureCounter(int key) {
		IntCounter valueCounter = counterMap.get(key);
	    if (valueCounter == null) {
	      valueCounter = new IntCounter();
	      counterMap.put(key, valueCounter);
	    }
	    return valueCounter;
	}
	
	public TIntSet keySet() {
		return counterMap.keySet();
	}
	
	public void setCount(int key, int value, double count) {
		IntCounter valueCounter = ensureCounter(key);
		valueCounter.setCount(value, count);
		currentModCount++;
	}
	
	public void incrementCount(int key, int value, double count) {
		IntCounter valueCounter = ensureCounter(key);
		valueCounter.incrementCount(value, count);
		currentModCount++;
	}
	
	public double getCount(int key, int value) {
		IntCounter valueCounter = counterMap.get(key);
		if (null == valueCounter) {
			return 0.0;
		}
		return valueCounter.getCount(value);
	}
	
	public IntCounter getCounter(int key) {
		return ensureCounter(key);
	}
	
	public boolean containsKey(int key) {
		return counterMap.containsKey(key);
	}
	
	public double totalCount() {
		if (currentModCount != cacheModCount) {
			double total = 0.0;
			for (TIntObjectIterator<IntCounter> it = counterMap.iterator(); it.hasNext();) {
				it.advance();
				IntCounter counter = it.value();
				total += counter.totalCount();
			}
			cacheTotalCount = total;
			cacheModCount = currentModCount;
		}
		return cacheTotalCount;
	}
	
	public int totalSize() {
		int total = 0;
		for (IntCounter counter : counterMap.values()) {
			total += counter.size();
		}
		return total;
	}
	
	public void normalize() {
		for (TIntObjectIterator<IntCounter> it = counterMap.iterator(); it.hasNext();) {
			it.advance();
			IntCounter counter = it.value();
			counter.normalize();
		}
		currentModCount++;
	}
	
	public void reset() {
		for (TIntObjectIterator<IntCounter> it = counterMap.iterator(); it.hasNext();) {
			it.advance();
			IntCounter counter = it.value();
			counter.reset();
		}
	}
	
	public int size() {
		return counterMap.size();
	}
	
	public boolean isEmpty() {
		return size() == 0;
	}
	
	public IntCounterMap() {
		counterMap = new TIntObjectHashMap<IntCounter>();
	}
}
